<?php
/*
    OpenLib
    Copyright (C) 2011  Michael Gukov, Pham Thi Thuy Duong

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


namespace Sun\Html;
use Sun\Html\Collections\LinkedList as LinkedList;
use Sun\Html\Collections\LinkedListNode as LinkedListNode;

abstract class Container extends Component implements IContainer
{
    private $items;
    private $list;
    private $added;

    ///////////////////////////////////////////////////////
    /////////////////////// PRIVATE ///////////////////////
    ///////////////////////////////////////////////////////
    private function validate_add($name)
    {
        if ($this->Contains($name))
            throw new Exception("Element with name = {$name} already added");
    }
    private function validate_get($name)
    {
        if (!$this->Contains($name))
            throw new Exception("Element with name = {$name} not found");
    }
    private function gen_name()
    {
        $i = count($this->items);
        while (array_key_exists($i, $this->items)) ++$i;
        return $i;
    }

    protected function add_item(IComponent $item, $name = null)
    {
        $this->validate_add($name);
        if ($name === null) $name = $this->gen_name();

        $node = new LinkedListNode($name, $item);
        $this->items[$name] = $node;
        $item->__affiliate($this, $name);
        $this->added = $item;
        return $node;
    }

    ///////////////////////////////////////////////////////
    /////////////////////// PUBLIC //////////////?/////////
    ///////////////////////////////////////////////////////
    public function __construct()
    {
        parent::__construct();
        $this->list = new LinkedList();
        $this->items = array();
    }

    public function __list()
    {
        return $this->list;
    }
    public function Add(IComponent $item, $name = null)
    {
        $this->list->AddLast($this->add_item($item, $name));
        return $this;
    }
    function AddFirst(IComponent $item, $name = null)
    {
        $this->list->AddFirst($this->add_item($item, $name));
        return $this;
    }
    function InsertAfter($after, IComponent $item, $name = null)
    {
        $this->validate_get($after);
        $after_key = $this->items[$after]->key;
        $this->list->InsertAfter($this->add_item($item, $name), $after_key);
        return $this;
    }
    public function AddRange(array $items)
    {
        foreach ($items as $name => $val)
        {
            $this->Add($val, $name);
        }
        return $this;
    }
    public function Remove($name)
    {
        $this->validate_get($name);
        $item = $this->items[$name];
        unset($this->items[$name]);
        $this->list->Remove($item->key);
        $item->__exclude();
    }
    public function Replace($first, IComponent $second, $name = null)
    {
        $this->validate_get($first);
        $node = $this->items[$first];

        if ($node->key === $this->list->First()->key)
        {
            $this->Remove($node->key);
            $this->AddFirst($second, $name);
        }
        else
        {
            $prev = $node->prev;
            $this->Remove($node->key);
            $this->InsertAfter($prev->key, $second, $name);
        }
    }
    public function Get($name)
    {
        $this->validate_get($name);
        return $this->items[$name]->value;
    }
    public function First()
    {
        if ($this->Count() == 0) return null;
        return $this->list->First()->value;
    }
    public function Last()
    {
        if ($this->Count() == 0) return null;
        return $this->list->Last()->value;
    }
    public function Count()
    {
        return count($this->items);
    }
    public function Childs()
    {
        return $this->list->ToArray();
    }
    public function Added()
    {
        return $this->added;
    }
    public function HtmlContent() {
        $out = "";
        $node = $this->list->First();
        while (isset($node))
        {
            $out .= $node->value->ToHtml();
            $node = $node->next;
        }
        return $out;
    }

    public function JSONContent() {
        $node = $this->list->First();
        $out = array();
        while (isset($node))
        {
            $out[] = $node->value->ToJSON();
            $node = $node->next;
        }
        return "[ ".implode(",", $out)." ]";
    }

    public function ToHtml()
    {
        return $this->HtmlContent();
    }
    public function ToJSON()
    {
        return $this->JSONContent();
    }
    public function Fetch($path)
    {
        $items = explode("/", $path);
        $curr = $this;
        foreach ($items as $i)
        {
            if ($curr->Contains($i)) $curr = $curr->Get($i);
            else return null;
        }
        return $curr;
    }
    public function Contains($name)
    {
        return array_key_exists($name, $this->items);
    }
    public function Copy()
    {
        return null;
    }
}

